﻿//==============================================================================
// File: PropertyMapper.cs
// Created: 2010-05-10
// Author: Piotr Włodek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with WpfRcp. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using WpfRcp.PropertyModel.Properties;

namespace WpfRcp.PropertyModel
{
    /// <summary>
    /// Static utility class which copies properties from one <see cref="IPropertyContainer"/> to another.
    /// </summary>
    public static class PropertyMapper
    {
        /// <summary>
        /// Copies values of all permanent (but not read-only), attached and dynamic properties from source to target.
        /// If dynamic or attached property is missing in target, it is automatically added. Source and target must be
        /// of exact same type.
        /// </summary>
        /// <param name="source">Source instance for property copying</param>
        /// <param name="target">Target instance for property copying</param>
        public static void CopyProperties(IPropertyContainer source, IPropertyContainer target)
        {
            if (source.GetType() != target.GetType())
                throw new ArgumentException(Resources.ExceptionInstancesTypesNotSame);

            foreach (var metaProperty in source.GetProperties())
            {
                object value = source.GetValue(metaProperty);

                switch(metaProperty.PropertyKind)
                {
                    case MetaPropertyKind.Permanent:
                        // We can only copy not read only properties
                        if (!metaProperty.IsReadOnly)
                        {
                            target.SetValue(metaProperty, value);
                        }
                        break;

                    case MetaPropertyKind.Dynamic:
                        // Check if property is registered, if not, register and set value
                        MetaProperty targetProperty = target.GetProperty(metaProperty.Name);
                        if(targetProperty == null)
                        {
                            targetProperty = target.RegisterDynamic(metaProperty.Name, metaProperty.PropertyType,
                                metaProperty.GetMetadata(source.GetType()));
                        }
                        target.SetValue(targetProperty, value);
                        break;

                    case MetaPropertyKind.Attached:
                        // Simply set attached property value
                        target.SetValue(metaProperty, value);
                        break;

                    case MetaPropertyKind.Transient:
                        // Transient properties are readonly. Do nothing
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// Copies values of all permanent (but not read-only), attached and dynamic properties from source to target.
        /// If dynamic or attached property is missing in target, it is automatically added. If any dynamic or attached 
        /// property exists in target, but not in source, it is deleted. Source and target must be of exact same type.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        public static void RestoreProperties(IPropertyContainer source, IPropertyContainer target)
        {
            CopyProperties(source, target);

            var sourceProperties = source.GetProperties();

            foreach (var metaProperty in target.GetProperties())
            {
                switch (metaProperty.PropertyKind)
                {
                    case MetaPropertyKind.Permanent:
                        break;

                    case MetaPropertyKind.Dynamic:
                        MetaProperty targetMetaProperty = metaProperty;
                        // Dynamic properties should be compared by name
                        if(sourceProperties.Find(sourceMetaProperty => sourceMetaProperty.Name == targetMetaProperty.Name) == null)
                        {
                            target.UnregisterDynamic(metaProperty);
                        }
                        break;

                    case MetaPropertyKind.Attached:
                        // Attached properties can be compared by instance (equality)
                        if(!sourceProperties.Contains(metaProperty))
                        {
                            target.ClearValue(metaProperty);
                        }
                        break;

                    case MetaPropertyKind.Transient:
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }
    }
}
